/**
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.xume.raob.annotations;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.xume.raob.api.BuildStep;
import org.xume.raob.api.BuildStepFactory;

/**
 * @author Johan Siebens
 */
public final class MVELBuildStepFactory implements BuildStepFactory<MVEL> {

	private static Map<String, Serializable> compiledExpressions = new ConcurrentHashMap<String, Serializable>();

	private static Serializable compileExpression(String expr) {
		Serializable compiledExpression = compiledExpressions.get(expr);
		if (compiledExpression == null) {
			compiledExpression = org.mvel2.MVEL.compileExpression(expr);
			compiledExpressions.put(expr, compiledExpression);
		}
		return compiledExpression;
	}

	private Serializable expression;

	private Map<String, Integer> variableNames = new HashMap<String, Integer>();

	public BuildStep[] getBuildSteps(Object... args) {
		Map<String, Object> variables = new HashMap<String, Object>();
		Set<Entry<String, Integer>> set = variableNames.entrySet();
		for (Entry<String, Integer> entry : set) {
			variables.put(entry.getKey(), args[entry.getValue()]);
		}
		return new BuildStep[] { new MVELBuildStep(expression, variables) };
	}

	public void initialize(MVEL mvel, Method builderMethod, Class<?> targetClass) {
		Annotation[][] annotations = builderMethod.getParameterAnnotations();
		int i = 0;
		for (Annotation[] an : annotations) {
			for (Annotation annotation : an) {
				if (annotation.annotationType().equals(Variable.class)) {
					String name = ((Variable) annotation).value();
					variableNames.put(name, i);
				}
			}
			i++;
		}

		this.expression = compileExpression(mvel.value());
	}

	public static class MVELBuildStep implements BuildStep {

		private Serializable expression;

		private Map<String, Object> variables;

		public MVELBuildStep(Serializable expression, Map<String, Object> variables) {
			this.expression = expression;
			this.variables = variables;
		}

		public void execute(Object target) {
			org.mvel2.MVEL.executeExpression(expression, target, variables);
		}

	}

}
